<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>BGM合成工具</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/ffmpeg/0.11.6/ffmpeg.min.js"></script>
    <style>
      * {
        box-sizing: border-box;
        margin: 0;
        padding: 0;
        font-family: 'SF Pro Display', 'PingFang SC', 'Helvetica Neue', Arial,
          sans-serif;
      }

      :root {
        --primary-bg: #e6e7ee;
        --secondary-bg: #ffffff;
        --shadow-light: #ffffff;
        --shadow-dark: #d1d9e6;
        --accent-color: #667eea;
        --accent-gradient: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
        --success-color: #4ecdc4;
        --danger-color: #ff6b6b;
        --warning-color: #feca57;
        --text-primary: #2d3748;
        --text-secondary: #718096;
        --glass-bg: rgba(255, 255, 255, 0.25);
        --glass-border: rgba(255, 255, 255, 0.18);
      }

      body {
        background: var(--primary-bg);
        color: var(--text-primary);
        min-height: 100vh;
        font-size: 14px;
        transition: all 0.3s ease;
        animation: pageLoad 0.8s ease;
      }

      @keyframes pageLoad {
        from {
          opacity: 0;
          transform: translateY(20px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      .header {
        text-align: center;
        padding: 40px 20px;
        background: var(--primary-bg);
        animation: headerSlide 0.8s ease 0.2s both;
      }

      @keyframes headerSlide {
        from {
          opacity: 0;
          transform: translateY(-30px);
        }
        to {
          opacity: 1;
          transform: translateY(0);
        }
      }

      h1 {
        font-size: 3.2rem;
        margin-bottom: 15px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
        font-weight: 700;
        animation: titleGlow 3s ease-in-out infinite alternate;
      }

      @keyframes titleGlow {
        from {
          filter: brightness(1);
        }
        to {
          filter: brightness(1.2);
        }
      }

      .subtitle {
        font-size: 1.3rem;
        color: var(--text-secondary);
        margin-bottom: 30px;
        font-weight: 500;
      }

      .app-container {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 30px;
        max-width: 1400px;
        margin: 0 auto;
        padding: 0 20px 40px;
        animation: panelsSlide 0.8s ease 0.6s both;
      }

      @media (max-width: 1024px) {
        .app-container {
          grid-template-columns: 1fr;
          gap: 25px;
        }
      }

      @keyframes panelsSlide {
        from {
          opacity: 0;
          transform: translateX(-20px);
        }
        to {
          opacity: 1;
          transform: translateX(0);
        }
      }

      .panel {
        background: var(--primary-bg);
        border-radius: 25px;
        padding: 30px;
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
        transition: all 0.3s ease;
        position: relative;
        overflow: hidden;
      }

      .panel::before {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.1),
          transparent
        );
        transition: left 0.8s;
      }

      .panel:hover::before {
        left: 100%;
      }

      .panel:hover {
        transform: translateY(-5px);
        box-shadow: 16px 16px 32px var(--shadow-dark),
          -16px -16px 32px var(--shadow-light);
      }

      .panel-title {
        font-size: 1.5rem;
        font-weight: 600;
        color: var(--accent-color);
        margin-bottom: 25px;
        background: var(--accent-gradient);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        background-clip: text;
      }

      .upload-area {
        border: 3px dashed var(--accent-color);
        border-radius: 20px;
        padding: 40px 20px;
        text-align: center;
        margin-bottom: 25px;
        transition: all 0.3s ease;
        cursor: pointer;
        position: relative;
        overflow: hidden;
      }

      .upload-area:hover {
        border-color: var(--success-color);
        background: rgba(102, 126, 234, 0.05);
        transform: translateY(-2px);
      }

      .upload-area.dragover {
        border-color: var(--success-color);
        background: rgba(78, 205, 196, 0.1);
        transform: scale(1.02);
      }

      .upload-icon {
        font-size: 3rem;
        color: var(--accent-color);
        margin-bottom: 15px;
        transition: all 0.3s ease;
      }

      .upload-area:hover .upload-icon {
        color: var(--success-color);
        transform: scale(1.1);
      }

      .upload-text {
        font-size: 1.1rem;
        color: var(--text-secondary);
        margin-bottom: 10px;
      }

      .upload-hint {
        font-size: 0.9rem;
        color: var(--text-secondary);
        opacity: 0.7;
      }

      .file-input {
        display: none;
      }

      .video-preview {
        width: 100%;
        max-height: 300px;
        border-radius: 15px;
        margin-bottom: 20px;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
      }

      .audio-list {
        max-height: 400px;
        overflow-y: auto;
        margin-bottom: 25px;
      }

      .audio-item {
        background: rgba(255, 255, 255, 0.05);
        border-radius: 15px;
        padding: 20px;
        margin-bottom: 15px;
        border: 1px solid rgba(255, 255, 255, 0.1);
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
        transition: all 0.3s ease;
      }

      .audio-item:hover {
        transform: translateY(-2px);
        box-shadow: inset 8px 8px 16px var(--shadow-dark),
          inset -8px -8px 16px var(--shadow-light),
          0 8px 20px rgba(0, 0, 0, 0.1);
      }

      .audio-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin-bottom: 15px;
      }

      .audio-name {
        font-weight: 600;
        color: var(--text-primary);
        font-size: 0.95rem;
      }

      .remove-btn {
        background: var(--danger-color);
        color: white;
        border: none;
        padding: 6px 12px;
        border-radius: 10px;
        cursor: pointer;
        font-size: 0.8rem;
        font-weight: 600;
        transition: all 0.3s ease;
      }

      .remove-btn:hover {
        background: #ff5252;
        transform: scale(1.05);
      }

      .audio-controls {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 15px;
        margin-bottom: 15px;
      }

      .control-group {
        display: flex;
        flex-direction: column;
        gap: 8px;
      }

      .control-label {
        font-size: 0.85rem;
        color: var(--text-secondary);
        font-weight: 500;
      }

      .control-input {
        background: var(--primary-bg);
        border: none;
        padding: 10px 12px;
        border-radius: 10px;
        color: var(--text-primary);
        font-size: 0.9rem;
        box-shadow: inset 4px 4px 8px var(--shadow-dark),
          inset -4px -4px 8px var(--shadow-light);
        transition: all 0.3s ease;
      }

      .control-input:focus {
        outline: none;
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light),
          0 0 0 2px rgba(102, 126, 234, 0.2);
      }

      .volume-slider {
        width: 100%;
        height: 6px;
        border-radius: 3px;
        background: var(--primary-bg);
        outline: none;
        cursor: pointer;
        -webkit-appearance: none;
        appearance: none;
        box-shadow: inset 3px 3px 6px var(--shadow-dark),
          inset -3px -3px 6px var(--shadow-light);
      }

      .volume-slider::-webkit-slider-thumb {
        -webkit-appearance: none;
        appearance: none;
        width: 18px;
        height: 18px;
        border-radius: 50%;
        background: var(--accent-color);
        cursor: pointer;
        box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
        transition: all 0.2s ease;
      }

      .volume-slider::-webkit-slider-thumb:hover {
        transform: scale(1.2);
        background: #7c8ef7;
      }

      .volume-display {
        text-align: center;
        font-size: 0.8rem;
        color: var(--text-secondary);
        margin-top: 5px;
      }

      .btn {
        background: var(--primary-bg);
        color: var(--text-primary);
        border: none;
        padding: 14px 28px;
        border-radius: 15px;
        cursor: pointer;
        font-weight: 600;
        transition: all 0.3s ease;
        font-size: 1rem;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
        position: relative;
        overflow: hidden;
      }

      .btn::before {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.2),
          transparent
        );
        transition: left 0.5s;
      }

      .btn:hover::before {
        left: 100%;
      }

      .btn:hover {
        transform: translateY(-3px);
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light);
      }

      .btn:active {
        transform: translateY(0);
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
      }

      .btn-primary {
        background: var(--accent-gradient);
        color: white;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2);
      }

      .btn-primary:hover {
        box-shadow: 12px 12px 24px var(--shadow-dark),
          -12px -12px 24px var(--shadow-light),
          inset 0 1px 0 rgba(255, 255, 255, 0.2),
          0 0 30px rgba(102, 126, 234, 0.3);
      }

      .btn-success {
        background: var(--success-color);
        color: white;
      }

      .btn:disabled {
        opacity: 0.6;
        cursor: not-allowed;
        transform: none;
      }

      .btn:disabled:hover {
        transform: none;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
      }

      .progress-container {
        margin: 25px 0;
        padding: 20px;
        background: rgba(255, 255, 255, 0.05);
        border-radius: 15px;
        border: 1px solid rgba(255, 255, 255, 0.1);
        box-shadow: inset 6px 6px 12px var(--shadow-dark),
          inset -6px -6px 12px var(--shadow-light);
        display: none;
      }

      .progress-bar {
        width: 100%;
        height: 20px;
        background: var(--primary-bg);
        border-radius: 10px;
        overflow: hidden;
        box-shadow: inset 4px 4px 8px var(--shadow-dark),
          inset -4px -4px 8px var(--shadow-light);
        margin-bottom: 10px;
      }

      .progress-fill {
        height: 100%;
        background: var(--accent-gradient);
        width: 0%;
        transition: width 0.3s ease;
        border-radius: 10px;
        box-shadow: 0 2px 10px rgba(102, 126, 234, 0.3);
        position: relative;
        overflow: hidden;
      }

      .progress-fill::after {
        content: '';
        position: absolute;
        top: 0;
        left: -100%;
        width: 100%;
        height: 100%;
        background: linear-gradient(
          90deg,
          transparent,
          rgba(255, 255, 255, 0.3),
          transparent
        );
        animation: progressShine 2s infinite;
      }

      @keyframes progressShine {
        0% {
          left: -100%;
        }
        100% {
          left: 100%;
        }
      }

      .progress-text {
        text-align: center;
        color: var(--text-secondary);
        font-size: 0.9rem;
        font-weight: 500;
      }

      .log-container {
        margin-top: 25px;
        padding: 20px;
        background: rgba(0, 0, 0, 0.8);
        border-radius: 15px;
        max-height: 200px;
        overflow-y: auto;
        font-family: 'Courier New', monospace;
        font-size: 0.8rem;
        line-height: 1.4;
        color: #00ff00;
        display: none;
      }

      .log-entry {
        margin-bottom: 5px;
        word-wrap: break-word;
      }

      .log-entry.error {
        color: #ff6b6b;
      }

      .log-entry.warning {
        color: #feca57;
      }

      .log-entry.info {
        color: #4ecdc4;
      }

      .preview-container {
        margin-top: 25px;
        display: none;
      }

      .preview-video {
        width: 100%;
        max-height: 300px;
        border-radius: 15px;
        box-shadow: 8px 8px 16px var(--shadow-dark),
          -8px -8px 16px var(--shadow-light);
      }

      .action-buttons {
        display: flex;
        gap: 15px;
        justify-content: center;
        margin-top: 25px;
        flex-wrap: wrap;
      }

      .status-message {
        position: fixed;
        top: 20px;
        right: 20px;
        padding: 15px 25px;
        border-radius: 15px;
        color: white;
        font-weight: 600;
        z-index: 1000;
        transform: translateX(400px);
        transition: transform 0.3s ease;
        box-shadow: 8px 8px 16px rgba(0, 0, 0, 0.2);
      }

      .status-message.show {
        transform: translateX(0);
      }

      .status-message.success {
        background: var(--success-color);
      }

      .status-message.error {
        background: var(--danger-color);
      }

      .status-message.warning {
        background: var(--warning-color);
      }

      .status-message.info {
        background: var(--accent-color);
      }

      @media (max-width: 768px) {
        .app-container {
          padding: 0 15px 30px;
        }

        .panel {
          padding: 20px;
        }

        h1 {
          font-size: 2.5rem;
        }

        .audio-controls {
          grid-template-columns: 1fr;
        }

        .action-buttons {
          flex-direction: column;
        }
      }
    </style>
  </head>
  <body>
    <div class="header">
      <h1>🎵 BGM合成工具</h1>
      <p class="subtitle">为您的视频添加完美的背景音乐</p>
    </div>

    <div class="app-container">
      <!-- 左侧面板 - 视频上传和预览 -->
      <div class="panel">
        <h2 class="panel-title">📹 视频文件</h2>
        
        <div class="upload-area" id="videoUploadArea">
          <div class="upload-icon">📁</div>
          <div class="upload-text">点击或拖拽上传视频文件</div>
          <div class="upload-hint">支持 MP4, AVI, MOV, MKV 等格式</div>
          <input type="file" class="file-input" id="videoInput" accept="video/*">
        </div>

        <div id="videoPreviewContainer" style="display: none;">
          <video class="video-preview" id="videoPreview" controls></video>
          <div style="text-align: center; margin-top: 10px;">
            <span id="videoInfo" style="color: var(--text-secondary); font-size: 0.9rem;"></span>
          </div>
        </div>

        <!-- 预览容器 -->
        <div class="preview-container" id="previewContainer">
          <h3 style="color: var(--accent-color); margin-bottom: 15px;">🎬 合成预览</h3>
          <video class="preview-video" id="previewVideo" controls></video>
        </div>

        <!-- 操作按钮 -->
        <div class="action-buttons">
          <button class="btn btn-primary" id="composeBtn" disabled>
            🎵 开始合成
          </button>
          <button class="btn" id="downloadLogBtn" style="display: none;">
            📋 下载日志
          </button>
        </div>

        <!-- 进度条 -->
        <div class="progress-container" id="progressContainer">
          <div class="progress-bar">
            <div class="progress-fill" id="progressFill"></div>
          </div>
          <div class="progress-text" id="progressText">准备中...</div>
        </div>

        <!-- 日志容器 -->
        <div class="log-container" id="logContainer">
          <div id="logContent"></div>
        </div>
      </div>

      <!-- 右侧面板 - 音频管理 -->
      <div class="panel">
        <h2 class="panel-title">🎵 音频文件管理</h2>
        
        <div class="upload-area" id="audioUploadArea">
          <div class="upload-icon">🎵</div>
          <div class="upload-text">点击或拖拽添加音频文件</div>
          <div class="upload-hint">支持 MP3, WAV, AAC, OGG 等格式</div>
          <input type="file" class="file-input" id="audioInput" accept="audio/*" multiple>
        </div>

        <div class="audio-list" id="audioList">
          <!-- 音频项目将在这里动态添加 -->
        </div>

        <div style="text-align: center; color: var(--text-secondary); font-size: 0.9rem; margin-top: 20px;">
          已添加 <span id="audioCount">0</span> 个音频文件
        </div>
      </div>
    </div>

    <!-- 状态消息 -->
    <div class="status-message" id="statusMessage"></div>

    <script>
      class BGMComposer {
        constructor() {
          this.ffmpeg = null;
          this.videoFile = null;
          this.audioFiles = [];
          this.logs = [];
          this.isProcessing = false;
          
          this.initElements();
          this.bindEvents();
          this.initFFmpeg();
        }

        initElements() {
          // 视频相关元素
          this.videoUploadArea = document.getElementById('videoUploadArea');
          this.videoInput = document.getElementById('videoInput');
          this.videoPreview = document.getElementById('videoPreview');
          this.videoPreviewContainer = document.getElementById('videoPreviewContainer');
          this.videoInfo = document.getElementById('videoInfo');
          
          // 音频相关元素
          this.audioUploadArea = document.getElementById('audioUploadArea');
          this.audioInput = document.getElementById('audioInput');
          this.audioList = document.getElementById('audioList');
          this.audioCount = document.getElementById('audioCount');
          
          // 控制元素
          this.composeBtn = document.getElementById('composeBtn');
          this.downloadLogBtn = document.getElementById('downloadLogBtn');
          
          // 进度和日志
          this.progressContainer = document.getElementById('progressContainer');
          this.progressFill = document.getElementById('progressFill');
          this.progressText = document.getElementById('progressText');
          this.logContainer = document.getElementById('logContainer');
          this.logContent = document.getElementById('logContent');
          
          // 预览
          this.previewContainer = document.getElementById('previewContainer');
          this.previewVideo = document.getElementById('previewVideo');
          
          // 状态消息
          this.statusMessage = document.getElementById('statusMessage');
        }

        bindEvents() {
          // 视频上传事件
          this.videoUploadArea.addEventListener('click', () => {
            this.videoInput.click();
          });
          
          this.videoInput.addEventListener('change', (e) => {
            this.handleVideoUpload(e.target.files[0]);
          });
          
          // 视频拖拽事件
          this.videoUploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            this.videoUploadArea.classList.add('dragover');
          });
          
          this.videoUploadArea.addEventListener('dragleave', () => {
            this.videoUploadArea.classList.remove('dragover');
          });
          
          this.videoUploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            this.videoUploadArea.classList.remove('dragover');
            const files = e.dataTransfer.files;
            if (files.length > 0 && files[0].type.startsWith('video/')) {
              this.handleVideoUpload(files[0]);
            }
          });
          
          // 音频上传事件
          this.audioUploadArea.addEventListener('click', () => {
            this.audioInput.click();
          });
          
          this.audioInput.addEventListener('change', (e) => {
            this.handleAudioUpload(Array.from(e.target.files));
          });
          
          // 音频拖拽事件
          this.audioUploadArea.addEventListener('dragover', (e) => {
            e.preventDefault();
            this.audioUploadArea.classList.add('dragover');
          });
          
          this.audioUploadArea.addEventListener('dragleave', () => {
            this.audioUploadArea.classList.remove('dragover');
          });
          
          this.audioUploadArea.addEventListener('drop', (e) => {
            e.preventDefault();
            this.audioUploadArea.classList.remove('dragover');
            const files = Array.from(e.dataTransfer.files).filter(file => 
              file.type.startsWith('audio/')
            );
            if (files.length > 0) {
              this.handleAudioUpload(files);
            }
          });
          
          // 合成按钮
          this.composeBtn.addEventListener('click', () => {
            this.startComposition();
          });
          
          // 下载日志按钮
          this.downloadLogBtn.addEventListener('click', () => {
            this.downloadLogs();
          });
        }

        async initFFmpeg() {
          try {
            this.addLog('正在初始化 FFmpeg...', 'info');
            
            if (typeof FFmpeg === 'undefined') {
              throw new Error('FFmpeg 库未加载');
            }
            
            this.ffmpeg = new FFmpeg.FFmpeg();
            
            this.ffmpeg.on('log', ({ message }) => {
              this.addLog(message, 'info');
            });
            
            this.ffmpeg.on('progress', ({ progress }) => {
              if (this.isProcessing) {
                this.updateProgress(Math.round(progress * 100), '正在处理视频...');
              }
            });
            
            await this.ffmpeg.load();
            this.addLog('FFmpeg 初始化完成', 'info');
            this.showStatus('FFmpeg 初始化完成', 'success');
          } catch (error) {
            this.addLog(`FFmpeg 初始化失败: ${error.message}`, 'error');
            this.showStatus('FFmpeg 初始化失败', 'error');
          }
        }

        handleVideoUpload(file) {
          if (!file || !file.type.startsWith('video/')) {
            this.showStatus('请选择有效的视频文件', 'error');
            return;
          }
          
          this.videoFile = file;
          this.addLog(`视频文件已选择: ${file.name} (${this.formatFileSize(file.size)})`, 'info');
          
          // 显示视频预览
          const url = URL.createObjectURL(file);
          this.videoPreview.src = url;
          this.videoPreviewContainer.style.display = 'block';
          
          // 获取视频信息
          this.videoPreview.addEventListener('loadedmetadata', () => {
            const duration = this.formatTime(this.videoPreview.duration);
            const resolution = `${this.videoPreview.videoWidth}x${this.videoPreview.videoHeight}`;
            this.videoInfo.textContent = `时长: ${duration} | 分辨率: ${resolution} | 大小: ${this.formatFileSize(file.size)}`;
          });
          
          this.updateComposeButton();
          this.showStatus('视频文件上传成功', 'success');
        }

        handleAudioUpload(files) {
          files.forEach(file => {
            if (!file.type.startsWith('audio/')) {
              this.showStatus(`跳过非音频文件: ${file.name}`, 'warning');
              return;
            }
            
            const audioId = Date.now() + Math.random();
            const audioData = {
              id: audioId,
              file: file,
              name: file.name,
              insertTime: 0,
              duration: 0,
              useFullDuration: true,
              volume: 50
            };
            
            this.audioFiles.push(audioData);
            this.addAudioItem(audioData);
            this.addLog(`音频文件已添加: ${file.name} (${this.formatFileSize(file.size)})`, 'info');
          });
          
          this.updateAudioCount();
          this.updateComposeButton();
          this.showStatus(`成功添加 ${files.length} 个音频文件`, 'success');
        }

        addAudioItem(audioData) {
          const audioItem = document.createElement('div');
          audioItem.className = 'audio-item';
          audioItem.dataset.id = audioData.id;
          
          audioItem.innerHTML = `
            <div class="audio-header">
              <div class="audio-name">${audioData.name}</div>
              <button class="remove-btn" onclick="bgmComposer.removeAudio(${audioData.id})">
                ❌ 移除
              </button>
            </div>
            
            <div class="audio-controls">
              <div class="control-group">
                <label class="control-label">插入时间 (秒)</label>
                <input type="number" class="control-input" min="0" step="0.1" 
                       value="${audioData.insertTime}" 
                       onchange="bgmComposer.updateAudioSetting(${audioData.id}, 'insertTime', this.value)">
              </div>
              
              <div class="control-group">
                <label class="control-label">使用时长 (秒)</label>
                <input type="number" class="control-input" min="0" step="0.1" 
                       value="${audioData.duration}" 
                       placeholder="0 = 使用全部时长"
                       onchange="bgmComposer.updateAudioSetting(${audioData.id}, 'duration', this.value)">
              </div>
            </div>
            
            <div class="control-group">
              <label class="control-label">音量: <span class="volume-display">${audioData.volume}%</span></label>
              <input type="range" class="volume-slider" min="0" max="100" 
                     value="${audioData.volume}" 
                     oninput="bgmComposer.updateAudioVolume(${audioData.id}, this.value)">
            </div>
          `;
          
          this.audioList.appendChild(audioItem);
        }

        updateAudioSetting(audioId, setting, value) {
          const audio = this.audioFiles.find(a => a.id === audioId);
          if (audio) {
            audio[setting] = parseFloat(value) || 0;
            this.addLog(`更新音频设置: ${audio.name} - ${setting} = ${value}`, 'info');
          }
        }

        updateAudioVolume(audioId, volume) {
          const audio = this.audioFiles.find(a => a.id === audioId);
          if (audio) {
            audio.volume = parseInt(volume);
            const volumeDisplay = document.querySelector(`[data-id="${audioId}"] .volume-display`);
            if (volumeDisplay) {
              volumeDisplay.textContent = `${volume}%`;
            }
          }
        }

        removeAudio(audioId) {
          this.audioFiles = this.audioFiles.filter(a => a.id !== audioId);
          const audioItem = document.querySelector(`[data-id="${audioId}"]`);
          if (audioItem) {
            audioItem.remove();
          }
          
          this.updateAudioCount();
          this.updateComposeButton();
          this.addLog(`音频文件已移除`, 'info');
          this.showStatus('音频文件已移除', 'success');
        }

        updateAudioCount() {
          this.audioCount.textContent = this.audioFiles.length;
        }

        updateComposeButton() {
          this.composeBtn.disabled = !this.videoFile || this.audioFiles.length === 0 || this.isProcessing;
        }

        async startComposition() {
          if (!this.ffmpeg || !this.videoFile || this.audioFiles.length === 0) {
            this.showStatus('请确保已上传视频文件和音频文件', 'error');
            return;
          }
          
          this.isProcessing = true;
          this.updateComposeButton();
          this.showProgress(true);
          this.showLogs(true);
          
          try {
            this.addLog('开始BGM合成处理...', 'info');
            this.updateProgress(10, '准备文件...');
            
            // 写入视频文件
            const videoData = new Uint8Array(await this.videoFile.arrayBuffer());
            await this.ffmpeg.writeFile('input.mp4', videoData);
            this.addLog('视频文件已加载到FFmpeg', 'info');
            
            // 写入音频文件并构建FFmpeg命令
            let audioInputs = [];
            let filterComplex = [];
            let audioMaps = [];
            
            this.updateProgress(20, '处理音频文件...');
            
            for (let i = 0; i < this.audioFiles.length; i++) {
              const audio = this.audioFiles[i];
              const audioData = new Uint8Array(await audio.file.arrayBuffer());
              const audioFileName = `audio_${i}.${this.getFileExtension(audio.file.name)}`;
              
              await this.ffmpeg.writeFile(audioFileName, audioData);
              audioInputs.push('-i', audioFileName);
              
              // 构建音频滤镜
              let audioFilter = `[${i + 1}:a]`;
              
              // 设置音量
              if (audio.volume !== 100) {
                audioFilter += `volume=${audio.volume / 100}`;
              }
              
              // 设置延迟（插入时间）
              if (audio.insertTime > 0) {
                audioFilter += `,adelay=${audio.insertTime * 1000}|${audio.insertTime * 1000}`;
              }
              
              // 设置持续时间
              if (audio.duration > 0) {
                audioFilter += `,atrim=0:${audio.duration}`;
              }
              
              audioFilter += `[a${i}]`;
              filterComplex.push(audioFilter);
              audioMaps.push(`[a${i}]`);
              
              this.addLog(`音频文件 ${i + 1} 已处理: ${audio.name}`, 'info');
            }
            
            this.updateProgress(40, '构建合成命令...');
            
            // 混合所有音频
            const mixFilter = `${audioMaps.join('')}amix=inputs=${this.audioFiles.length}:duration=longest[mixed]`;
            filterComplex.push(mixFilter);
            
            // 与原视频音频混合
            filterComplex.push('[0:a][mixed]amix=inputs=2:duration=longest[final]');
            
            // 构建完整的FFmpeg命令
            const command = [
              '-i', 'input.mp4',
              ...audioInputs,
              '-filter_complex', filterComplex.join(';'),
              '-map', '0:v',
              '-map', '[final]',
              '-c:v', 'copy',
              '-c:a', 'aac',
              '-b:a', '192k',
              'output.mp4'
            ];
            
            this.addLog(`执行FFmpeg命令: ffmpeg ${command.join(' ')}`, 'info');
            this.updateProgress(50, '开始视频合成...');
            
            // 执行FFmpeg命令
            await this.ffmpeg.exec(command);
            
            this.updateProgress(90, '准备下载文件...');
            
            // 读取输出文件
            const outputData = await this.ffmpeg.readFile('output.mp4');
            
            // 创建下载链接
            const blob = new Blob([outputData], { type: 'video/mp4' });
            const url = URL.createObjectURL(blob);
            
            // 自动下载
            const a = document.createElement('a');
            a.href = url;
            a.download = `bgm_composed_${Date.now()}.mp4`;
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            
            // 显示预览
            this.previewVideo.src = url;
            this.previewContainer.style.display = 'block';
            
            this.updateProgress(100, '合成完成!');
            this.addLog('BGM合成完成，文件已自动下载', 'info');
            this.showStatus('BGM合成完成！', 'success');
            
            // 显示下载日志按钮
            this.downloadLogBtn.style.display = 'inline-block';
            
            setTimeout(() => {
              this.showProgress(false);
            }, 2000);
            
          } catch (error) {
            this.addLog(`合成失败: ${error.message}`, 'error');
            this.showStatus('合成失败', 'error');
            this.updateProgress(0, '合成失败');
            
            setTimeout(() => {
              this.showProgress(false);
            }, 3000);
          } finally {
            this.isProcessing = false;
            this.updateComposeButton();
          }
        }

        addLog(message, type = 'info') {
          const timestamp = new Date().toLocaleTimeString();
          const logEntry = {
            timestamp,
            message,
            type
          };
          
          this.logs.push(logEntry);
          
          const logElement = document.createElement('div');
          logElement.className = `log-entry ${type}`;
          logElement.textContent = `[${timestamp}] ${message}`;
          
          this.logContent.appendChild(logElement);
          this.logContainer.scrollTop = this.logContainer.scrollHeight;
        }

        showProgress(show) {
          this.progressContainer.style.display = show ? 'block' : 'none';
        }

        updateProgress(percent, text) {
          this.progressFill.style.width = `${percent}%`;
          this.progressText.textContent = text;
        }

        showLogs(show) {
          this.logContainer.style.display = show ? 'block' : 'none';
        }

        downloadLogs() {
          const logText = this.logs.map(log => 
            `[${log.timestamp}] [${log.type.toUpperCase()}] ${log.message}`
          ).join('\n');
          
          const blob = new Blob([logText], { type: 'text/plain;charset=utf-8' });
          const url = URL.createObjectURL(blob);
          
          const a = document.createElement('a');
          a.href = url;
          a.download = `bgm_composer_log_${Date.now()}.txt`;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          
          URL.revokeObjectURL(url);
          this.showStatus('日志文件已下载', 'success');
        }

        showStatus(message, type = 'info') {
          this.statusMessage.textContent = message;
          this.statusMessage.className = `status-message ${type} show`;
          
          setTimeout(() => {
            this.statusMessage.classList.remove('show');
          }, 3000);
        }

        formatFileSize(bytes) {
          if (bytes === 0) return '0 Bytes';
          const k = 1024;
          const sizes = ['Bytes', 'KB', 'MB', 'GB'];
          const i = Math.floor(Math.log(bytes) / Math.log(k));
          return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
        }

        formatTime(seconds) {
          const hours = Math.floor(seconds / 3600);
          const minutes = Math.floor((seconds % 3600) / 60);
          const secs = Math.floor(seconds % 60);
          
          if (hours > 0) {
            return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
          }
          return `${minutes}:${secs.toString().padStart(2, '0')}`;
        }

        getFileExtension(filename) {
          return filename.split('.').pop().toLowerCase();
        }
      }

      // 初始化应用
      const bgmComposer = new BGMComposer();
    </script>
  </body>
</html>